package com.jackstraw.jack_base.remote_command.entity;

import cn.hutool.extra.ssh.ChannelType;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import com.jackstraw.jack_base.remote_command.pool.SessionConnectionPool;
import com.jackstraw.jack_base.util.SpringUtil;
import com.jackstraw.jack_base.util.exception.BadRequestException;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Session;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;

/**
 *
 */
@Data
@Slf4j
public class Conn {
    private String connName;
    private SessionConnectionPool sessionConnectionPool;
    private Session session;
    private ChannelExec channelExec;
    private ChannelSftp channelSftp;
    private ChannelShell channelShell;
    private Sftp sftp;
    private ChannelType channelType;

    private Conn(String connName, ChannelType channelType) throws Exception {
        this.setConnName(connName);
        this.setChannelType(channelType);
    }

    private Conn setConnName(String connName) throws Exception {
        this.connName = connName;
        SessionConnectionPool sessionConnectionPool = SpringUtil.getBean(connName
                .concat(SessionConnectionPool.SESSIONCONNECTIONPOOL_BEANNAME), SessionConnectionPool.class);
        if (ObjectUtils.isEmpty(sessionConnectionPool)) {
            log.error("无法通过connName获取SessionConnectionPool，传入的connName为：{}", connName);
            throw new BadRequestException("0100000006", "无法通过connName获取SessionConnectionPool，传入的connName为：{}", connName);
        }
        this.setSessionConnectionPool(sessionConnectionPool);
        return this;
    }

    private Conn setSession(Session session) {
        this.session = session;
        return this;
    }

    private Conn setChannelType(ChannelType channelType) {
        this.channelType = channelType;
        this.setChannel(channelType);
        return this;
    }

    private Conn setChannelExec(ChannelExec channelExec) {
        this.channelExec = channelExec;
        return this;
    }

    private Conn setChannelSftp(ChannelSftp channelSftp) {
        this.channelSftp = channelSftp;
        return this;
    }

    private Conn setChannelShell(ChannelShell channelShell) {
        this.channelShell = channelShell;
        return this;
    }

    private Conn setSftp(Sftp sftp) {
        this.sftp = sftp;
        this.setChannelSftp(this.getSftp().getClient());
        return this;
    }

    private Conn setSessionConnectionPool(SessionConnectionPool sessionConnectionPool) throws Exception {
        this.sessionConnectionPool = sessionConnectionPool;
        this.setSession(this.getSessionConnectionPool().borrowObject());
        return this;
    }


    private Conn setChannel(ChannelType channelType) {
        if (ChannelType.SFTP.equals(channelType)) {
            this.setSftp();
        } else if (ChannelType.EXEC.equals(channelType)) {
            this.setChannelExec((ChannelExec) JschUtil.createChannel(this.getSession(), channelType));
        } else if (ChannelType.SHELL.equals(channelType)) {
            this.setChannelShell((ChannelShell) JschUtil.createChannel(this.getSession(), channelType));
        }
        return this;
    }


    private Conn setSftp() {
        this.setSftp(new Sftp(this.getSession()));
        return this;
    }

    public static Conn build(String connName, ChannelType channelType) throws Exception {
        return new Conn(connName, channelType);
    }

    public static Conn buildExec(String connName) throws Exception {
        return build(connName, ChannelType.EXEC);
    }

    public static Conn buildSftp(String connName) throws Exception {
        return build(connName, ChannelType.SFTP);
    }

    public static Conn buildShell(String connName) throws Exception {
        return build(connName, ChannelType.SHELL);
    }


    public void close() {
        if (ObjectUtils.isNotEmpty(this.channelExec) && this.channelExec.isConnected()) {
            this.channelExec.disconnect();
        }
        if (ObjectUtils.isNotEmpty(this.channelSftp) && this.channelSftp.isConnected()) {
            this.channelSftp.disconnect();
        }
        if (ObjectUtils.isNotEmpty(this.channelShell) && this.channelShell.isConnected()) {
            this.channelShell.disconnect();
        }
        if (ObjectUtils.isNotEmpty(sessionConnectionPool)) {
            sessionConnectionPool.returnObject(this.getSession());
        }
    }

    public ChannelExec getChannelExec() {
        if (ObjectUtils.isEmpty(this.channelExec) || this.channelExec.isClosed()) {
            this.setChannel(ChannelType.EXEC);
        }
        return this.channelExec;
    }

    public ChannelSftp getChannelSftp() {
        if (ObjectUtils.isEmpty(this.channelSftp) || this.channelSftp.isClosed()) {
            this.setChannel(ChannelType.SFTP);
        }
        return this.channelSftp;
    }

    public ChannelShell getChannelShell() {
        if (ObjectUtils.isEmpty(this.channelShell) || this.channelShell.isClosed()) {
            this.setChannel(ChannelType.SHELL);
        }
        return this.channelShell;
    }

    public Sftp getSftp() {
        if (ObjectUtils.isEmpty(this.sftp)) {
            this.setChannel(ChannelType.SFTP);
        }
        return sftp;
    }

}
